package com.gusto.engine.clusterant.algorithms.antsclustering.impl;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.gusto.engine.clusterant.access.DistanceDAO;
import com.gusto.engine.clusterant.access.PlanDAO;
import com.gusto.engine.clusterant.algorithms.antsclustering.AntsClusteringAlgorithm;
import com.gusto.engine.clusterant.algorithms.antsclustering.Plan;
import com.gusto.engine.clusterant.algorithms.antsclustering.ants.Ant;

/**
 * <p>Generic {@link AntsClusteringAlgorithm} that provides the common behavior.</p>
 * 
 * @author amokrane.belloui@gmail.com
 *
 */
public abstract class GenericAntsClusteringAlgorithm implements AntsClusteringAlgorithm {
	 
	private Logger log = Logger.getLogger(getClass());
	 
	protected PlanDAO planDAO;
	protected DistanceDAO distanceDAO;
	
	protected Plan plan;
	protected int ROWS = 20;
	protected int COLS = 20;
	protected List<Ant> ants = new ArrayList<Ant>(); 
	
	protected Long start = null;
	protected Long end = null;
	
	public void setPlanDAO(PlanDAO planDAO) {
		this.planDAO = planDAO;
	}
	
	public void setDistanceDAO(DistanceDAO distanceDAO) {
		this.distanceDAO = distanceDAO;
	}
	
	public Plan getPlan() {
		return plan;
	}
	
	public int getROWS() {
		return ROWS;
	}
	
	public void setROWS(int rows) {
		ROWS = rows;
	}
	
	public int getCOLS() {
		return COLS;
	}
	
	public void setCOLS(int cols) {
		COLS = cols;
	}
	
	
	public void initializeRandom(List<Object> points, int minDist) {
		plan = new Plan(planDAO, distanceDAO, ROWS, COLS);
		plan.initialize(points, minDist);
	}
	
	public void initializeLoadData(List<Object> points) {
		plan = new Plan(planDAO, distanceDAO, ROWS, COLS);
		plan.loadPoints();
	}
	
	public void cluster() {
		log.info("Started Clustering " + plan.toString());
		start = System.currentTimeMillis();
		end = null;
		
		this.doCluster();
	}
	
	public void addAnt() {
		log.info("Adding an Ant");
		if (end != null) {
			start = System.currentTimeMillis();
			end = null;
		}
		
		this.doAddAnt();
	}
	
	/**
	 * Method to override to implement the beginning of clustering 
	 */
	protected abstract void doCluster();
	
	/**
	 * Method to override to implement the addition of an Ant
	 */
	protected abstract void doAddAnt();
	
	protected final boolean isAllAntsFinishedWork(List<Ant> ants) {
		for (Ant ant : ants) {
			if (!ant.isFinishedJob()) {
				return false;
			}
		}
		end = System.currentTimeMillis();
		log.info("Clustering done in " + (System.currentTimeMillis() - start) + " millis");
		log.info("Plan " + plan);
		return true;
	}
	
	public Integer getActiveAntsCount() {
		Integer count = 0;
		for (Ant ant : ants) {
			if (!ant.isFinishedJob()) {
				count ++;
			}
		}
		return count;
	}
	
	public Long getTotalMovesDone() {
		Long count = 0L;
		for (Ant ant : ants) {
			count += ant.getMoves();
		}
		return count;
	}
	
	public Long getTotalMovesLeft() {
		Long count = 0L;
		for (Ant ant : ants) {
			count += (ant.getMaxMoves() - ant.getMoves());
		}
		return count;
	}
	
	public Long getTotalTime() {
		if (start != null) {
			if (end != null) {
				return (end - start);
			}
			else {
				return (System.currentTimeMillis() - start);
			}
		}
		else {
			return 0L;
		}
	}
	
}
